﻿Imports System.Drawing.Drawing2D

Public Class Grid : Inherits DataGridView

	Private _buttons As Object

	Event Activate(ByVal sender As Object)
	Event ButtonClicked(ByVal Cell As DataGridViewCell)
	Event Delete(ByVal sender As Object)

	Public HasEditableRows As Boolean
	Public KeyColumn As String = ""

	Property HandleEnter As Boolean
	Event Propegate(sender As Object, e As KeyEventArgs)

	ReadOnly Property ExcelCursor As Windows.Forms.Cursor
		Get
			Static Cursor As Cursor = Nothing
			If Cursor Is Nothing Then
				Dim B As New Bitmap(16, 16)
				Dim G = Graphics.FromImage(B)
				Dim r1 = New Rectangle(4, 0, 7, 15)
				Dim r2 = New Rectangle(0, 4, 15, 7)
				For i = 1 To 3
					Dim Path As New GraphicsPath(FillMode.Winding)
					Path.AddRectangle(r1)
					Path.AddRectangle(r2)
					Dim c As Color = Choose(i, Color.Black.Opacity(64), Color.Black, Color.White)
					G.FillPath(c.Brush, Path)
					r1.Inflate(-1, -1)
					r2.Inflate(-1, -1)
				Next
				Cursor = New Cursor(B.GetHicon)
			End If
			Return Cursor
		End Get
	End Property

	Dim _ShowLineNumbers As Boolean
	Property ShowLineNumbers As Boolean
		Get
			Return _ShowLineNumbers
		End Get
		Set(value As Boolean)
			_ShowLineNumbers = value
			If value Then
				RowHeadersVisible = True
				Invalidate()
			End If
		End Set
	End Property

	Property HideZeros As Boolean

	Dim AutoFilterColumns As New List(Of String)
	Dim _AutoFilter As String
	Property AutoFilter As String
		Get
			Return _AutoFilter
		End Get
		Set(value As String)
			_AutoFilter = value
			AutoFilterColumns.Clear()
			For Each Col In value.Split(",")
				AutoFilterColumns.Add(Col)
			Next
			Invalidate()
		End Set
	End Property

	Property AutoNavigate As Boolean

	Property AlwaysShowFocused As Boolean

	ReadOnly Property BottomPanel As Panel
		Get
			Return Box.BottomPanel
		End Get
	End Property

	ReadOnly Property LeftPanel As Panel
		Get
			Return Box.LeftPanel
		End Get
	End Property

	ReadOnly Property Box As Box
		Get
			Return Parent
		End Get
	End Property

	ReadOnly Property Buttons(p1 As String) As ToolStripItem
		Get
			Return Box.Buttons(p1)
		End Get
	End Property

	Protected Overrides Sub OnKeyPress(e As System.Windows.Forms.KeyPressEventArgs)
		If AutoNavigate Then
			Dim ind = If(SelectedRows.Count = 0, 0, SelectedRows(0).Index + 1)
			Dim ci = 0
			For Each Column As DataGridViewColumn In Columns
				If Column.Visible Then ci = Column.Index : Exit For
			Next
			For j = 0 To Rows.Count - 1
				Dim i = (j + ind) Mod Rows.Count
				If Rows(i).Visible AndAlso LCase(Mid(Rows(i).Cells(ci).Value, 1, 1)) = LCase(e.KeyChar) Then
					HighlightRow(i)
					Return
				End If
			Next
		End If
		'If Not CurrentCell Is Nothing AndAlso Not CurrentCell.ReadOnly Then
		'	e.Handled = True
		'	BeginEdit(True)
		'	SendKeys.Send(e.KeyChar)
		'	'CType(EditingControl, Object).SelectionStart = 1
		'	Return
		'End If
		MyBase.OnKeyPress(e)
	End Sub

	Protected Overrides Sub OnCellMouseDoubleClick(e As System.Windows.Forms.DataGridViewCellMouseEventArgs)
		MyBase.OnCellMouseDoubleClick(e)
		BeginEdit(True)
	End Sub

	Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
		If keyData = Keys.Control + Keys.F Then
			Box.ShowFind()
			Return True
		End If
		If keyData = Keys.Enter Then
			Dim e As New KeyEventArgs(Keys.Enter)
			RaiseEvent Propegate(Me, e)
			If IsCurrentCellInEditMode Then EndEdit()
			If e.Handled Then Return True
		End If
		Return MyBase.ProcessCmdKey(msg, keyData)
	End Function

	Protected Overrides Function ProcessDialogKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean
		If keyData = Keys.Enter Then
			Dim e As New KeyEventArgs(Keys.Enter)
			RaiseEvent Propegate(Me, e)
			If e.Handled Then Return True
			If HandleEnter Then
				RaiseEvent Activate(Me)
				Return True
			Else
				RaiseEvent Activate(Me)
				If Me.Focused Then Return MyBase.ProcessDialogKey(Keys.Tab)
			End If
		ElseIf keyData = Keys.Delete Then
			If IsCurrentCellInEditMode Then Exit Function
			RaiseEvent Delete(Me)
		Else
			Return MyBase.ProcessDialogKey(keyData)
		End If
	End Function

	Shadows Function IsCurrentCellDirty() As Boolean
		If Not IsCurrentCellInEditMode Then Return False
		Return Not Trim(EditingControl.Text) = Trim(CurrentCell.Value)
	End Function

	Protected Overrides Sub OnCellMouseEnter(e As System.Windows.Forms.DataGridViewCellEventArgs)
		MyBase.OnCellMouseEnter(e)
		If e.ColumnIndex < 0 Or e.RowIndex < 0 Then Return
		Dim c = Rows(e.RowIndex).Cells(e.ColumnIndex)
		If c.ReadOnly Then Return
		If TypeOf c Is DataGridViewCheckBoxCell Then Return
		If TypeOf c Is DataGridViewButtonCell Then Return
		If TypeOf c Is GridCell AndAlso CType(c, GridCell).Type = GridColumn.ContentType.Combo Then Return
		Cursor = ExcelCursor
	End Sub

	Protected Overrides Sub OnCellMouseLeave(e As System.Windows.Forms.DataGridViewCellEventArgs)
		MyBase.OnCellMouseLeave(e)
		Cursor = Cursors.Default
	End Sub

	Protected Overrides Sub OnCellClick(e As System.Windows.Forms.DataGridViewCellEventArgs)
		MyBase.OnCellClick(e)
		If e.ColumnIndex >= 0 Then
			If TypeOf Columns(e.ColumnIndex) Is DataGridViewButtonColumn Then
				RaiseEvent ButtonClicked(Rows(e.RowIndex).Cells(e.ColumnIndex))
			End If
		Else
			EndEdit()
		End If
	End Sub

	Dim m_Sortable As Boolean = True
	Property Sortable() As Boolean
		Get
			Return m_Sortable
		End Get
		Set(ByVal Value As Boolean)
			m_Sortable = Value
			For Each Column As DataGridViewColumn In Columns
				Column.SortMode = DataGridViewColumnSortMode.NotSortable
			Next
		End Set
	End Property

	Public Sub AddColumns(ByVal Text As String)
		Dim Pos As Integer = 0
		Static Alignments As String = "lrcwnmd"
		For Each Item As Object In Split(Text, ",")
			Item = Trim(Item)
			Pos = InStr(Item, "(")
			Dim Name As String = Trim(Mid(Item, 1, Pos - 1))
			Item = Mid(Item, Pos + 1)
			Pos = InStrRev(Item, ")")
			Item = Mid(Item, 1, Pos - 1)
			If Item = "h" Then Item = ";h;0"
			Dim Data = Split(Item, ";")
			If Data.Count = 2 Then
				If Alignments.Contains(Mid(Data(1), Len(Data(1)), 1)) Then
					Data(1) = Mid(Data(1), Len(Data(1)), 1) & ";" & Mid(Data(1), 1, Len(Data(1)) - 1)
				ElseIf Alignments.Contains(Mid(Data(1), 1, 1)) Then
					Data(1) = Mid(Data(1), 1, 1) & ";" & Mid(Data(1), 2)
				Else
					Data(1) = "l;" & Data(1)
				End If
				Data = Join(Data, ";").Split(";")
			End If
			AddColumn(Name, Trim(Data(0)), Trim(Data(1)), Trim(Data(2)))
		Next
	End Sub

	Public Sub AddColumn(ByVal Name As String, ByVal Caption As String, ByVal Alignment As String, ByVal Width As String)
		Dim Col As Object
		Dim Format As String = ""
		If Name.Contains("[pk]") Then
			Name = Name.Replace("[pk]", "")
			KeyColumn = Name
		End If
		If Mid(Name, 1, 1) = "*" Then
			If Alignment = "m" Then
				Col = New GridColumn(GridColumn.ContentType.Money)
			ElseIf Alignment = "d" Then
				Col = New GridColumn(GridColumn.ContentType.Date)
			ElseIf Alignment = "a" Then
				Col = New GridColumn(GridColumn.ContentType.Date)
			Else
				Col = New GridColumn(GridColumn.ContentType.Text)
			End If
			HasEditableRows = True
			Name = Mid(Name, 2)
			Col.ReadOnly = False
		ElseIf Mid(Name, 1, 1) = "+" Then
			Col = New GridColumn(GridColumn.ContentType.Combo)
			HasEditableRows = True
			Name = Mid(Name, 2)
			Col.ReadOnly = False
		ElseIf Mid(Name, 1, 1) = "^" Then
			Col = New DataGridViewCheckBoxColumn
			HasEditableRows = True
			Name = Mid(Name, 2)
			Col.ReadOnly = False
		ElseIf Mid(Name, 1, 1) = "#" Then
			Col = New DataGridViewButtonColumn
			Name = Mid(Name, 2)
			Col.ReadOnly = False
		Else
			Col = New GridColumn(GridColumn.ContentType.Text)
			Col.ReadOnly = True
		End If
		Col.Name = Name
		Col.DataPropertyName = Name
		Col.HeaderText = Caption
		If Not Sortable Then
			Col.SortMode = DataGridViewColumnSortMode.NotSortable
		Else
			Col.SortMode = DataGridViewColumnSortMode.Programmatic
		End If
		If Width.StartsWith("%") Or Width.EndsWith("%") Then
			Width = Width.Replace("%", "")
			Col.FillWeight = Val(Width)
			If Val(Width) < 3 Then Exit Sub
			Col.MinimumWidth = Val(Width) / 2
		Else
			Col.Width = Val(Width)
			Col.Resizable = DataGridViewTriState.False
			Col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None
		End If
		Select Case LCase(Alignment)
			Case "", "left", "l" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
			Case "right", "r" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
			Case "center", "c" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
			Case "d" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter : Format = "d"
			Case "a" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter : Format = "a"
			Case "n" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight : Format = "n"
			Case "m" : Col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight : Format = "m"
			Case "h" : Col.Visible = False
		End Select
		Col.HeaderCell.Style.Alignment = Col.DefaultCellStyle.Alignment
		Columns.Add(Col)
		If TypeName(Format) = "String" Then
			Select Case Format
				Case "number", "n" : Format = "###,###,###,###,##0"
				Case "money", "m" : Format = "###,###,###,###,##0.00"
				Case "date", "d" : Format = "dd/MM/yyyy"
				Case "a" : Format = "yyyy/MM"
			End Select
		End If
		If TypeName(Format) = "String" Then
			Col.DefaultCellStyle.Format = Format
		End If
		If HasEditableRows Then
			SelectionMode = DataGridViewSelectionMode.CellSelect
			RowHeadersVisible = True
		End If
	End Sub

	Sub DrawCellButton(ByVal G As Graphics, ByVal R As Rectangle, ByVal Cell As DataGridViewCell)
		DrawCell(G, R, "", Cell)
		If Cell.FormattedValue = "" Then Exit Sub
		R.Inflate(-3, -1)
		R.Height -= 1
		G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
		FillRoundRect(G, R, Brushes.DarkOliveGreen, 4)
		FillRoundRect(G, R, LGBv(R, Color.FromArgb(48, Color.White), Color.FromArgb(48, Color.Black)), 4)
		DrawRoundRect(G, R, New Pen(Color.FromArgb(192, Color.Black)), 4)
		R.Inflate(-1, -1)
		DrawRoundRect(G, R, New Pen(Color.FromArgb(48, Color.White)), 3)
		G.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
		Dim Format As StringFormat = Centered
		Format.Trimming = StringTrimming.EllipsisWord
		Format.FormatFlags = Format.FormatFlags Or StringFormatFlags.NoWrap
		DrawStringShadowed(G, Split(Cell.FormattedValue & "|", "|")(1), Font, Color.White, R, Format)
	End Sub

	Sub DrawCellCheck(ByVal G As Graphics, ByVal R As Rectangle, ByVal Cell As DataGridViewCell, ByVal Value As String)
		G.FillRectangle(Brushes.White, R)
		Dim SelectionBrush As Brush = Hlnbrush
		If Focused Then SelectionBrush = Hlfbrush
		If Cell.Selected Then
			G.FillRectangle(SelectionBrush, R)
		ElseIf Rows(Cell.RowIndex).Selected Then
			G.FillRectangle(SelectionBrush, R)
		ElseIf Columns(Cell.ColumnIndex).Selected Then
			G.FillRectangle(SelectionBrush, R)
		End If
		R.Width -= 1 : R.Height -= 1
		Dim Pen As New Pen(Color.FromArgb(64, Color.Black))
		G.DrawLine(Pen, R.Right, R.Top, R.Right, R.Bottom)
		G.DrawLine(Pen, R.Right - 1, R.Bottom, R.Left, R.Bottom)
		R.Inflate(-(R.Width - 12) \ 2, -(R.Height - 12) \ 2)
		If Value = "True" Then
			Windows.Forms.CheckBoxRenderer.DrawCheckBox(G, R.Location, VisualStyles.CheckBoxState.CheckedNormal)
		Else
			Windows.Forms.CheckBoxRenderer.DrawCheckBox(G, R.Location, VisualStyles.CheckBoxState.UncheckedNormal)
		End If
	End Sub

	Sub DrawCell(ByVal G As Graphics, R As Rectangle, ByVal Value As String, ByVal Cell As DataGridViewCell)
		Dim RRR = R
		G.SmoothingMode = SmoothingMode.Default
		G.PixelOffsetMode = PixelOffsetMode.Default
		G.FillRectangle(Cell.InheritedStyle.BackColor.Brush, R)
		If HasEditableRows Then R.Width -= 1 : R.Height -= 1
		Dim Selected As Boolean = Not Cell.IsInEditMode And Cell.Selected OrElse Rows(Cell.RowIndex).Selected OrElse Columns(Cell.ColumnIndex).Selected
		If Selected Then
			Dim RR = R
			'If IsFirstVisibleCell(Cell.ColumnIndex) Then RR.Offset(1, 0) : RR.Width -= 1
			'If IsLastVisibleCell(Cell.ColumnIndex) Then RR.Width -= 1
			G.FillRectangle(If(Focused OrElse AlwaysShowFocused, SystemColors.Highlight.Opacity(128).Brush, Hlnbrush), RR)
		End If
		If Cell.IsInEditMode AndAlso TypeOf Cell.OwningColumn Is GridColumn AndAlso CType(Cell.OwningColumn, GridColumn).Type = GridColumn.ContentType.Combo Then
			Dim RR = R
			G.FillRectangle(SystemColors.Highlight.Opacity(128).Brush, RR)
		End If
		If HasEditableRows Then R.Width += 1 : R.Height += 1
		If HasEditableRows Then
			R.Width -= 1 : R.Height -= 1
			Dim Pen2 As New Pen(Color.FromArgb(64, Color.Black))
			G.DrawLine(Pen2, R.Right, R.Top, R.Right, R.Bottom)
			G.DrawLine(Pen2, R.Right - 1, R.Bottom, R.Left, R.Bottom)
		End If
		Dim Pen As New Pen(Color.FromArgb(64, Color.Black)) 'With {.DashStyle = DashStyle.Dot}
		If Selected And (Focused Or AlwaysShowFocused) And Not HasEditableRows Then
			Dim RR = R
			G.SmoothingMode = SmoothingMode.None
			If SelectionMode = DataGridViewSelectionMode.CellSelect OrElse Cell.RowIndex = 0 OrElse Not Rows(Cell.RowIndex - 1).Selected Then G.DrawLine(Pen, RR.Left, RR.Top, RR.Right, RR.Top)
			If SelectionMode = DataGridViewSelectionMode.CellSelect OrElse Cell.RowIndex = RowCount - 1 OrElse Not Rows(Cell.RowIndex + 1).Selected Then G.DrawLine(Pen, RR.Left, RR.Bottom - 1, RR.Right, RR.Bottom - 1)
			If SelectionMode = DataGridViewSelectionMode.CellSelect OrElse IsFirstVisibleCell(Cell.ColumnIndex) Then G.DrawLine(Pen, RR.Left, RR.Top, RR.Left, RR.Bottom)
			If SelectionMode = DataGridViewSelectionMode.CellSelect OrElse IsLastVisibleCell(Cell.ColumnIndex) Then G.DrawLine(Pen, RR.Right - 1, RR.Top, RR.Right - 1, RR.Bottom)
		End If
		If Cell.Selected And Cell.IsInEditMode Then
			'R.Width -= 1 : R.Height -= 1
			'R.Inflate(1, 1)
			'G.DrawRectangle(Pens.Black, R)
			'R.Width += 1 : R.Height += 1
			'R.Inflate(-1, -1)
		End If
		Dim HasIcon As Boolean = False
		Select Case Mid(Value, 1, 3)
			Case "[x]", "[!]", "[/]", "[?]", "[-]", "[i]", "[+]"
				DrawCellIcon(G, R, Mid(Value, 2, 1))
				Value = Mid(Value, 4)
				R.Offset(R.Height, 0)
				R.Width -= R.Height
				HasIcon = True
		End Select
		Dim Format As StringFormat = StrFormat(Choose(Columns(Cell.ColumnIndex).DefaultCellStyle.Alignment / 16, "l", "c", "l", "r"), "m")
		Format.Trimming = StringTrimming.EllipsisWord
		Format.FormatFlags = Format.FormatFlags Or StringFormatFlags.NoWrap
		G.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
		'R = GetCellDisplayRectangle(Cell.ColumnIndex, Cell.RowIndex, False)
		R = RRR
		R.Offset(Cell.InheritedStyle.Padding.Left, Cell.InheritedStyle.Padding.Top)
		R.Width -= Cell.InheritedStyle.Padding.Horizontal
		R.Height -= Cell.InheritedStyle.Padding.Vertical
		If HasEditableRows Then
			R.Width -= 1 : R.Height -= 1
		End If
		G.SmoothingMode = SmoothingMode.None
		G.PixelOffsetMode = PixelOffsetMode.None
		'G.FillRectangle(Brushes.Blue, R)
		If HideZeros AndAlso Value = "0.00" Then Value = ""
		If HasIcon Then
			R.Offset(R.Height, 0)
			R.Width -= R.Height
		End If
		If Not Value = Nothing AndAlso Value.Contains("~") Then
			Dim Items = Value.Split("~")
			DrawText2(G, Items(0), New Font(Cell.InheritedStyle.Font, Font.Style + FontStyle.Strikeout), SystemColors.GrayText, R, Format)
			R.Offset(MeasureText(Items(0), Font).Width, 0)
			DrawText2(G, "  " & Items(1), Cell.InheritedStyle.Font, Color.Blue, R, Format)
		Else
			DrawText2(G, Value, Cell.InheritedStyle.Font, Cell.InheritedStyle.ForeColor, R, Format)
		End If
		R.Offset(-Cell.InheritedStyle.Padding.Left, 0)
		R.Width += Cell.InheritedStyle.Padding.Horizontal - 1
		R.Height += 1
	End Sub

	Function IsFirstVisibleCell(i As Integer) As Boolean
		Do Until i = 0
			If Columns(i - 1).Visible Then Return False
			i -= 1
		Loop
		Return True
	End Function

	Function IsLastVisibleCell(i As Integer) As Boolean
		Do Until i = ColumnCount - 1
			If Columns(i + 1).Visible Then Return False
			i += 1
		Loop
		Return True
	End Function

	Sub DrawCellIcon(ByVal G As Graphics, ByVal R As Rectangle, ByVal Icon As String)
		G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
		G.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
		G.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
		R.Width = R.Height
		R.Inflate(-3, -3)
		Dim Clr As Color = Color.Black
		G.InterpolationMode = InterpolationMode.HighQualityBilinear
		Select Case Icon
			Case "x" : R.Inflate(-2, -2) : G.DrawImage(My.Resources.ierror, R) : Return
			Case "!" : G.DrawImage(My.Resources.warning, R) : Return
			Case "/" : G.DrawImage(My.Resources.check, R) : Return
			Case "i" : G.DrawImage(My.Resources.Info, R) : Return
			Case "-" : G.DrawImage(My.Resources.norapplicable, R) : Return
		End Select
		Select Case Icon
			Case "-" : Clr = Color.Gray
			Case "+" : Clr = Color.Blue
		End Select
		G.FillEllipse(New SolidBrush(Color.FromArgb(96, Color.Black)), R)
		R.Offset(-1, -1)
		G.FillEllipse(New SolidBrush(Clr), R)
		G.FillEllipse(LGBv(R, Color.Transparent, Color.FromArgb(128, Color.Black)), R)
		R.Inflate(-1, -1)
		Dim Pen As New Pen(Color.White, 2)
		Pen.StartCap = Drawing2D.LineCap.Round
		Pen.EndCap = Drawing2D.LineCap.Round
		R.Inflate(-4, -4)
		Dim Fmt As StringFormat = Centered
		Select Case Icon
			Case "-" : G.DrawLine(Pen, R.Left, CInt(R.Top + R.Height / 2), R.Right, CInt(R.Top + R.Height / 2))
			Case "+" : G.DrawLine(Pen, R.Left, CInt(R.Top + R.Height / 2), R.Right, CInt(R.Top + R.Height / 2)) : G.DrawLine(Pen, CInt(R.Left + R.Width / 2), R.Top, CInt(R.Left + R.Width / 2), R.Bottom)
		End Select
	End Sub

	Protected Overrides Sub OnCellMouseDown(e As System.Windows.Forms.DataGridViewCellMouseEventArgs)
		If e.ColumnIndex >= 0 Then
			Dim Col = Columns(e.ColumnIndex)
			If e.RowIndex = -1 Then
				If AutoFilterColumns.Contains(Col.Name) AndAlso e.X > Col.Width - ColumnHeadersHeight Then
					ShowAutofilterDropdown(Col)
					Return
				End If
				If Col.SortMode = DataGridViewColumnSortMode.Programmatic Then
					Sort(Col, System.ComponentModel.ListSortDirection.Ascending)
					Return
				End If
			End If
			If TypeOf Col Is DataGridViewCheckBoxColumn Then
				'Dim c As DataGridViewCheckBoxCell=
				'Application.DoEvents()
			End If
		End If
		MyBase.OnCellMouseDown(e)
		If HasEditableRows Then
			If e.ColumnIndex = -1 Then
				EndEdit()
				SelectionMode = DataGridViewSelectionMode.RowHeaderSelect
				Exit Sub
			End If
			SelectionMode = DataGridViewSelectionMode.CellSelect
		End If
	End Sub

	Shadows ContextMenu As New ContextMenuStrip

	Sub ShowAutofilterDropdown(Col As DataGridViewColumn)
		ContextMenu.Items.Clear()
		ContextMenu.ShowImageMargin = False
		ContextMenu.Renderer = New Renderer
		ContextMenu.MinimumSize = New Size(Col.Width, 1)
		ContextMenu.Items.Add(New ToolStripMenuItem With {.Name = "(asc)", .Text = "Artan sırala", .Padding = Padding.Empty, .Margin = Padding.Empty})
		ContextMenu.Items.Add(New ToolStripMenuItem With {.Name = "(desc)", .Text = "Azalan sırala", .Padding = Padding.Empty, .Margin = Padding.Empty})
		ContextMenu.Items.Add("-")
		ContextMenu.Items.Add(New ToolStripMenuItem With {.Name = "(all)", .Text = "(Tüm kayıtlar)", .Padding = Padding.Empty, .Margin = Padding.Empty})
		Dim g = (From r As DataGridViewRow In Rows Where r.Visible Select v = r.Cells(Col.Name).Value Group By v Into c = Count() Order By v Descending)
		For Each a In g
			ContextMenu.Items.Add(New ToolStripMenuItem With {.Text = a.v, .Padding = Padding.Empty, .Margin = Padding.Empty})
		Next
		For Each Menu As ToolStripItem In ContextMenu.Items
			Menu.AutoSize = False
			Menu.Size = New Size(ContextMenu.Width, Menu.Height)
			If TypeOf Menu Is ToolStripMenuItem Then
				AddHandler Menu.Click, AddressOf ContextMenuClick
			End If
		Next
		Dim Rc = GetCellDisplayRectangle(Col.Index, -1, False)
		ContextMenu.Tag = Col
		ContextMenu.Show(Me, Rc.Left, Rc.Bottom - 1)
	End Sub

	Private Sub ContextMenuClick(sender As Object, e As EventArgs)
		Dim Col As DataGridViewColumn = ContextMenu.Tag
		Select Case sender.Name
			Case "(asc)"
				Sort(Col, System.ComponentModel.ListSortDirection.Ascending)
				Return
			Case "(desc)"
				Sort(Col, System.ComponentModel.ListSortDirection.Descending)
				Return
			Case "(all)"
				Col.Tag = ""
				ApplyFilter()
				Return
		End Select
		Col.Tag = "filter:" & sender.Text
		ApplyFilter()
	End Sub

	Sub ApplyFilter()
		For Each Row As DataGridViewRow In Rows
			Dim Visible = True
			For Each Column As DataGridViewColumn In Columns
				If Not Column.Tag Is Nothing AndAlso Column.Tag Like "filter:*" Then
					Dim fv = Mid(Column.Tag, 8)
					If Row.Cells(Column.Name).FormattedValue <> fv Then
						Visible = False
						Exit For
					End If
				End If
			Next
			Row.Visible = Visible
		Next
	End Sub

	Protected Overrides Sub OnCellPainting(e As System.Windows.Forms.DataGridViewCellPaintingEventArgs)
		MyBase.OnCellPainting(e)
		If e.Handled Then Return
		Dim R As Rectangle = e.CellBounds
		If e.RowIndex = -1 And e.ColumnIndex = -1 Then
			Themes.Current.PaintListHeader(e.Graphics, R)
		ElseIf e.RowIndex = -1 Then
			Themes.Current.PaintListColumnHeader(e.Graphics, R, Columns(e.ColumnIndex), AutoFilterColumns.Contains(Columns(e.ColumnIndex).Name))
		ElseIf e.ColumnIndex = -1 Then
			Themes.Current.PaintListRowHeader(e.Graphics, R, Rows(e.RowIndex))
			If ShowLineNumbers AndAlso (e.RowIndex < RowCount - 1 OrElse Not Rows(e.RowIndex).IsNewRow) Then
				Dim SFont = If(e.RowIndex >= 999, New Font(Font.Name, Font.Size - 2), If(e.RowIndex >= 99, New Font(Font.Name, Font.Size - 1), Font))
				TextRenderer.DrawText(e.Graphics, e.RowIndex + 1, SFont, R, Color.Black, Color.Transparent, TextFormatFlags.HorizontalCenter + TextFormatFlags.VerticalCenter)
			End If
		Else
			Dim Cell As DataGridViewCell = Rows(e.RowIndex).Cells(e.ColumnIndex)
			If TypeOf Columns(e.ColumnIndex) Is DataGridViewButtonColumn Then
				DrawCellButton(e.Graphics, R, Cell)
			ElseIf TypeOf Columns(e.ColumnIndex) Is DataGridViewCheckBoxColumn Then
				DrawCellCheck(e.Graphics, R, Cell, e.FormattedValue)
			Else
				DrawCell(e.Graphics, R, e.FormattedValue, Cell)
			End If
		End If
		e.Handled = True
	End Sub

	Private Sub InheritedGrid_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
		If e.KeyCode = Keys.Enter Then
			e.Handled = True
			RaiseEvent Activate(Me)
			If Me.Focused And Not HandleEnter Then SendKeys.Send("{TAB}")
		ElseIf e.KeyCode = Keys.Delete Then
			e.Handled = True
			RaiseEvent Delete(Me)
		End If
	End Sub

	Dim Hlfbrush As New SolidBrush(Color.FromArgb(80, Color.Blue))
	Dim Hlnbrush As Brush = SystemBrushes.ButtonFace

	Private Sub FocusHandler(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Leave, Me.Enter, Me.GotFocus, Me.LostFocus
		Invalidate()
	End Sub

	Public Sub New()
		DoubleBuffered = True
		RowHeadersVisible = False
		Dock = DockStyle.Fill
		BringToFront()
		AllowUserToAddRows = False
		AllowUserToDeleteRows = False
		AllowUserToResizeRows = False
		AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill
		BackgroundColor = System.Drawing.SystemColors.ControlLightLight
		BorderStyle = System.Windows.Forms.BorderStyle.None
		ColumnHeadersHeight = 22
		ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing
		EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnKeystroke
		EnableHeadersVisualStyles = False
		GridColor = System.Drawing.SystemColors.ButtonFace
		CellBorderStyle = DataGridViewCellBorderStyle.None
		MultiSelect = False
		RowHeadersVisible = False
		RowHeadersWidth = 22
		RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing
		SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect
		StandardTab = True
		AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
		'DefaultCellStyle.Padding = New Padding(0)
		RowTemplate.DefaultCellStyle.Padding = New Padding(4, 2, 4, 2)
		'RowTemplate.Height = 18
	End Sub

	Protected Overrides Sub OnRowsAdded(e As System.Windows.Forms.DataGridViewRowsAddedEventArgs)
		MyBase.OnRowsAdded(e)
		OnSelectionChanged(Nothing)
	End Sub

	Protected Overrides Sub OnRowStateChanged(rowIndex As Integer, e As System.Windows.Forms.DataGridViewRowStateChangedEventArgs)
		MyBase.OnRowStateChanged(rowIndex, e)
		If (e.StateChanged And DataGridViewElementStates.Selected) = 0 Then Exit Sub
		If rowIndex <> 0 Then InvalidateRow(rowIndex - 1)
		InvalidateRow(rowIndex)
		If rowIndex <> RowCount - 1 Then InvalidateRow(rowIndex + 1)
	End Sub

	Dim Stuck As Boolean
	Protected Overrides Sub OnSelectionChanged(e As System.EventArgs)
		MyBase.OnSelectionChanged(e)
		Static LastRowIndex As Integer = -1
		Stuck = False
		If SelectedRows.Count = 0 Then
			LastRowIndex = -1
			Exit Sub
		End If
		If Not TypeOf SelectedRows(0).Tag Is String OrElse Not SelectedRows(0).Tag.StartsWith("<title>") Then
			LastRowIndex = SelectedRows(0).Index
			Return
		End If
		If Not (From r As DataGridViewRow In Rows Where Not TypeOf r.Tag Is String OrElse Not r.Tag.StartsWith("<title>")).Any Then Return
		Dim Index As Integer = SelectedRows(0).Index
		Dim Offset As Integer = Math.Sign(Index - LastRowIndex)
		If Offset > 0 Then
			Dim c = (From r As DataGridViewRow In Rows Where r.Index > Index AndAlso (Not TypeOf r.Tag Is String OrElse Not r.Tag.StartsWith("<title>")) Select i = r.Index)
			If c.Any Then
				Index = c.First
			Else
				c = (From r As DataGridViewRow In Rows Where r.Index <= Index AndAlso (Not TypeOf r.Tag Is String OrElse Not r.Tag.StartsWith("<title>")) Select i = r.Index)
				If c.Any Then
					Index = c.Last
				Else
					Return
				End If
			End If
		ElseIf Offset < 0 Then
			Dim c = (From r As DataGridViewRow In Rows Where r.Index < Index AndAlso (Not TypeOf r.Tag Is String OrElse Not r.Tag.StartsWith("<title>")) Select i = r.Index)
			If c.Any Then
				Index = c.Last
			Else
				c = (From r As DataGridViewRow In Rows Where r.Index >= Index AndAlso (Not TypeOf r.Tag Is String OrElse Not r.Tag.StartsWith("<title>")) Select i = r.Index)
				If c.Any Then
					Index = c.First
				Else
					Return
				End If
			End If
		End If
		Rows(Index).Selected = True
		CurrentCell = Rows(Index).Cells(0)
		LastRowIndex = Index
	End Sub

	Protected Overrides Sub OnDataError(displayErrorDialogIfNoHandler As Boolean, e As System.Windows.Forms.DataGridViewDataErrorEventArgs)
		e.Cancel = True
	End Sub

	Dim OriginalValue As Object
	Protected Overrides Sub OnCellBeginEdit(e As System.Windows.Forms.DataGridViewCellCancelEventArgs)
		MyBase.OnCellBeginEdit(e)
		OriginalValue = Rows(e.RowIndex).Cells(e.ColumnIndex).Value
	End Sub

	Protected Overrides Sub OnCellEndEdit(e As System.Windows.Forms.DataGridViewCellEventArgs)
		MyBase.OnCellEndEdit(e)
		If OriginalValue Is Nothing Then Return

		If Trim(IsNull(Rows(e.RowIndex).Cells(e.ColumnIndex).Value, "")) <> Trim(IsNull(OriginalValue, "")) Then RaiseEvent CellValueEdited(Me)
	End Sub

	Event CellValueEdited(sender As Object)

	Public Sub HighlightRow(i As Integer)
		If i < 0 Then Return
		Dim Row = Rows(i)
		ClearSelection()
		Row.Selected = True
		For Each Cell As DataGridViewCell In Row.Cells
			If Cell.Visible Then CurrentCell = Cell
		Next
	End Sub

	Sub SelectFirst()
		Dim fr = (From r As DataGridViewRow In Rows Where r.Visible).FirstOrDefault
		If Not fr Is Nothing Then HighlightRow(fr.Index)
	End Sub

	Sub AddButton(p1 As String, p2 As String, Optional Image As Image = Nothing)
		Box.AddButton(p1, p2, Image)
	End Sub

	Sub AddButtons(p1 As String)
		Box.AddButtons(p1)
	End Sub

	Sub AddNote(p1 As String, p2 As String)
		Box.AddNote(p1, p2)
	End Sub

	Sub Clear()
		Rows.Clear()
	End Sub

	Function AddRow(ParamArray Values() As Object) As DataGridViewRow
		Rows.Add(Values)
		Return Rows(Rows.Count - 1)
	End Function

	Property AllowNewRow() As Boolean
		Get
			Return AllowUserToAddRows
		End Get
		Set(ByVal Value As Boolean)
			AllowUserToAddRows = Value
		End Set
	End Property

	WriteOnly Property ColumnSource(ByVal ColumnName As String) As Recordset
		Set(ByVal Value As Recordset)
			CType(Columns(ColumnName), GridColumn).DataSource = Value
		End Set
	End Property

	Public Sub Copy()
		Dim Text As String = ""
		Dim Line As String = ""
		For Each Column As DataGridViewColumn In Columns
			Line &= vbTab & Column.Name
		Next
		Text &= Mid(Line, 2)
		For Each Row As DataGridViewRow In Rows
			Line = ""
			For Each Cell As DataGridViewCell In Row.Cells
				Line &= vbTab & Cell.Value
			Next
			Text &= vbCrLf & Mid(Line, 2)
		Next
		Clipboard.Clear()
		Clipboard.SetText(Text)
	End Sub

	Property HeadersVisible() As Boolean
		Get
			Return ColumnHeadersVisible
		End Get
		Set(ByVal Value As Boolean)
			ColumnHeadersVisible = Value
			RowHeadersVisible = Value
		End Set
	End Property

	Public ReadOnly Property NoSelection() As Boolean
		Get
			Return SelectedRows.Count = 0
		End Get
	End Property

	Sub Populate(Optional ByVal PreserveSelection As Boolean = True)
		Dim Key As String = ""
		If Not NoSelection Then Key = SelectedRow(KeyColumn)
		DataSource = Token.OpenSQL(SQL)
		If Key = "" Then Exit Sub
		For i As Integer = 0 To Rows.Count - 1
			If Rows(i).Cells(KeyColumn).Value = Key Then
				ClearSelection()
				Rows(i).Selected = True
				For j = 0 To 999
					If Rows(i).Cells(j).Visible Then CurrentCell = Rows(i).Cells(j) : Exit For
				Next
				Exit For
			End If
		Next
	End Sub

	Sub RemoveSelected()
		Rows.Remove(SelectedRows(0))
	End Sub

	Public ReadOnly Property SelectedRow(ByVal FieldName) As Object
		<DebuggerHidden()> Get
			Return SelectedRows(0).Cells(FieldName).Value
		End Get
	End Property

	Dim Token As Token
	Dim SQL As String

	Sub SetSQL(Token As Token, SQL As String)
		Me.Token = Token
		Me.SQL = SQL
	End Sub

	Property Cells(ByVal Row, ByVal Column) As Object
		Get
			Return Rows(Row).Cells(Column).Value
		End Get
		Set(ByVal Value As Object)
			Rows(Row).Cells(Column).Value = Value
		End Set
	End Property

	Private Sub Grid_Activate(ByVal sender As Object) Handles Me.Activate
		If TypeOf Parent Is Box Then Box.RaiseActivateEvent()
	End Sub

	Private Sub Grid_Delete(ByVal sender As Object) Handles Me.Delete
		Box.RaiseActionEvent("Delete")
	End Sub

	Private Sub Grid_ButtonClicked(ByVal Cell As System.Windows.Forms.DataGridViewCell) Handles Me.ButtonClicked
		Box.RaiseActionEvent(Split(Cell.Value, "|")(0))
	End Sub

	Private Sub Grid_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DoubleClick
		Box.RaiseActivateEvent()
	End Sub

	Private Sub Grid_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
		If e.KeyChar = Chr(13) Then e.Handled = True
	End Sub

	Private Sub Grid_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SelectionChanged
		If TypeOf Parent Is Box Then Box.RaiseChangeEvent()
	End Sub

	Dim m_DataSource As Object
	Public Shadows Property DataSource() As Object
		Get
			Return m_DataSource
		End Get
		Set(ByVal Value As Object)
			Dim Table As DataTable = Nothing
			Select Case TypeName(Value)
				Case "Recordset"
					Table = Value
				Case "DataTable"
					Table = Value
			End Select
			If Not IsNothing(Table) Then
				SuspendLayout()
				Rows.Clear()
				For Each Column As DataColumn In Table.Columns
					If Not Columns.Contains(Column.ColumnName) Then
						Dim NewColumn As New DataGridViewTextBoxColumn
						NewColumn.Visible = False
						NewColumn.Name = Column.ColumnName
						NewColumn.DataPropertyName = Column.ColumnName
						Columns.Add(NewColumn)
					End If
				Next
				If Table.Rows.Count > 0 Then
					Rows.Add(Table.Rows.Count)
					For i As Integer = 0 To Table.Rows.Count - 1
						Dim Row As DataGridViewRow = Rows(i)
						For Each Column As DataColumn In Table.Columns
							Row.Cells(Column.ColumnName).Value = Table.Rows(i)(Column.ColumnName)
						Next
						If (i + 1) Mod 1000 = 0 Then
							ResumeLayout()
							Invalidate()
							SuspendLayout()
						End If
					Next
				End If
				'ProgressBar.Visible = False
				ResumeLayout()
				Invalidate()
				m_DataSource = Table
				If Table.Rows.Count > 0 Then
					'Grid.ClearSelection()
					Rows(0).Selected = True
					Box.RaiseChangeEvent()
				End If
				'Grid.DataSource = Table
			Else
				'If Not Parent Is Nothing Then Box.NotesPanel.Controls.Clear()
				'If Not Parent Is Nothing Then Box.NotesPanel.Visible = False
				Rows.Clear()
				MyBase.DataSource = Nothing
				If HasEditableRows Then
					AllowUserToAddRows = True
					SelectionMode = DataGridViewSelectionMode.CellSelect
					StandardTab = False
				End If
			End If
		End Set
	End Property

	Sub InsertRow(Index As Integer, ByVal ParamArray Values() As Object)
		Rows.Insert(Index, Values)
	End Sub

	Shadows Sub [Select](kod As String)
		Dim Row As DataGridViewRow = (From r In Rows Where r.Cells(KeyColumn).Value = kod).FirstOrDefault
		If Row Is Nothing Then Return
		ClearSelection()
		Row.Selected = True
		Dim Cell As DataGridViewCell = (From c In Row.Cells Where c.visible).First
		CurrentCell = Cell
	End Sub


	Sub Search(Text As String)
		For Each Row As DataGridViewRow In Rows
			Row.Visible = Text = "" OrElse (From c As DataGridViewCell In Row.Cells Where Not IsDBNull(c.Value) AndAlso Lower(c.Value) Like "*" & Lower(Text) & "*").Any
		Next
	End Sub

	Sub Emphasize(ByVal Row As DataGridViewRow)
		ClearSelection()
		Row.Selected = True
		FirstDisplayedScrollingRowIndex = Row.Index
	End Sub

	Sub SelectCell(ByVal Row, ByVal Column)
		Rows(Row).Cells(Column).Selected = True
	End Sub

	Sub Reset()
		Rows.Clear()
		DataSource = Nothing
		AllowNewRow = False
	End Sub


End Class




